package com.zgg.core.config

import com.zgg.MainStart
import com.zgg.core.annotation.HttpHandler
import com.zgg.core.annotation.HttpMethod
import com.zgg.core.annotation.Param
import com.zgg.core.exception.ClientException
import com.zgg.core.utils.Reflection
import io.vertx.core.Handler
import io.vertx.core.MultiMap
import io.vertx.core.json.JsonObject
import io.vertx.ext.web.Route
import io.vertx.ext.web.Router
import io.vertx.ext.web.RoutingContext
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.lang.reflect.Field
import kotlin.reflect.KClass
import kotlin.reflect.KParameter
import kotlin.reflect.KType
import kotlin.reflect.full.callSuspendBy
import kotlin.reflect.full.createInstance
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.functions

class RunStartScanHandler {
    companion object {
        /**
         * kotlin解析参数
         */
        @Throws(RuntimeException::class)
        suspend fun parseArgs(parameters: List<KParameter>, rc: RoutingContext, o: Any): Map<KParameter, Any?> {
            val args = hashMapOf<KParameter, Any?>()
            // 不支持多值。
            val params: MultiMap = rc.request().params()
            val queryObject = JsonObject()
            params.entries().forEach { entry -> queryObject.put(entry.key, entry.value) }
            val header: String = rc.request().getHeader("Content-Type")
            if (header != null && header.contains("json") && rc.getBody().length() !== 0) {
                val bodyAsJson: JsonObject = rc.bodyAsJson
                if (!bodyAsJson.isEmpty) {
                    queryObject.mergeIn(bodyAsJson)
                }
            }
            for (parameter in parameters) {
                if (parameter.kind == KParameter.Kind.INSTANCE) {
                    args[parameter] = o
                }
                val type: KType = parameter.type
                val name: String? = parameter.name

                if (type.classifier == RoutingContext::class) {
                    args[parameter] = rc
                    continue
                }
                if (!Reflection.isPrimitiveType(type)) {
                    //简单 bean
                    try {
                        val clazz = Class.forName(type.toString())
                        val declaredFields: Array<Field> = clazz.declaredFields
                        val beanMap = queryObject.asSequence().filter {
                            for (field in declaredFields) {
                                if (it.key == field.name) {
                                    return@filter true
                                }
                            }
                            return@filter false
                        }.associateBy({ it.key }, { it.value })
                        val beanJson = JsonObject(beanMap)
                        val bean = beanJson.mapTo(clazz)
                        for (field in declaredFields) {
                            val fieldAnnotation = field.getAnnotation(Param::class.java)
                            field.isAccessible = true
                            field.get(bean)?.let {
                                if (fieldAnnotation != null) {
                                    field.set(bean, fieldAnnotation.defaultValue)
                                } else {
                                    throw ClientException("request parameter. $name . ${field.getName()} null")
                                }
                            }

                        }
                        args[parameter] = bean
                        continue
                    } catch (e: RuntimeException) {
                        throw ClientException("parse bean error. ${e.message}")
                    } catch (e: IllegalAccessException) {
                        throw ClientException("parse bean error. ${e.message}")
                    }
                }
                //  基本类型
                var value = queryObject.getString(name)
                if (value == null) {
                    //看是否有默认值
                    val param: Param? = parameter.findAnnotation<Param>()
                    if (param != null) {
                        value = param.defaultValue
                    }
                    //if ()
                }
                if (type.classifier == String::class) {
                    args[parameter] = value
                } else if (type.classifier == Int::class || type.classifier == Int::class.javaPrimitiveType) {
                    args[parameter] = if (value.isEmpty()) null else value.toInt()
                } else if (type.classifier == Short::class || type.classifier == Short::class.javaPrimitiveType) {
                    args[parameter] = if (value.isEmpty()) null else value.toShort()
                } else if (type.classifier == Long::class || type.classifier == Long::class.javaPrimitiveType) {
                    args[parameter] = if (value.isEmpty()) null else value.toLong()
                } else if (type.classifier == Double::class || type.classifier == Double::class.javaPrimitiveType) {
                    args[parameter] = if (value.isEmpty()) null else value.toDouble()
                } else if (type.classifier == Float::class || type.classifier == Float::class.javaPrimitiveType) {
                    args[parameter] = if (value.isEmpty()) null else value.toFloat()
                } else if (type.classifier == Boolean::class || type.classifier == Boolean::class.javaPrimitiveType) {
                    args[parameter] = if (value.isEmpty()) null else java.lang.Boolean.getBoolean(value)
                } else if (type.classifier == Byte::class || type.classifier == Byte::class.javaPrimitiveType) {
                    args[parameter] = if (value.isEmpty()) null else value.toByte()
                } else {
                    throw ClientException("unsupported type:" + type.classifier + ":" + name + ":" + value)
                }
            }
            return args
        }

        @Throws(RuntimeException::class)
        suspend fun parseReturnValue(rc: RoutingContext, invoke: Any?, annotation: HttpHandler) {
            //在方法内处理完毕
            if (rc.response().ended()) return
            //同上
            if (invoke == null) {
                rc.addBodyEndHandler { if (!rc.response().ended()) rc.response().end() }
            } else {
                // 根据注解，处理返回类型。//现在默认只支持json类型
                var result = invoke.toString()
                if (annotation.produce.contains("application/json")) {
                    result = if (invoke is JsonObject || invoke is String) {
                        invoke.toString()
                    }
                    /*else if (invoke is Map<*, *>) {
                        JsonObject(invoke as Map<*, *>?).toString()
                    } else if (invoke is Array<*>) {
                        JsonArray(Arrays.asList(*invoke as Array<Any?>)).toString()
                    } else if (invoke is List<*>) {
                        JsonArray(invoke as List<*>?).toString()
                    } */
                    else {
                        // 尝试不支持类型
                        JsonObject.mapFrom(invoke).toString()
                    }
                    rc.response().putHeader("content-type", annotation.produce).end(result)
                }
                /* else if (annotation.produce().contains("text/html") && engine != null) {
                     //html
                     val engine: TemplateEngine? = getTemplateEngine()
                     engine.render(rc.data(), "templates/$result", { ar ->
                         if (ar.succeeded()) {
                             rc.response().putHeader("content-type", annotation.produce()).end(ar.result())
                         } else {
                             logger.error("render template error. {}", ar.cause().getMessage())
                             rc.response().setStatusCode(500).end(ar.cause().getMessage())
                         }
                     })
                 }*/
                else {
                    //默认 按 text/plain 处理
                    rc.response().putHeader("content-type", "text/plain; charset=utf-8;").end(result)
                }
            }
        }
    }
}